﻿namespace ElencySolutions.MultipleProperty
{
    using System;
    using System.Collections;
    using System.Collections.Generic;
    using System.Diagnostics;
    using System.IO;
    using System.Linq;
    using System.Reflection;
    using System.Text;
    using System.Web;
    using System.Web.UI;
    using System.Web.UI.HtmlControls;
    using System.Web.UI.WebControls;
    using System.Xml;
    using Configuration;
    using EPiServer;
    using EPiServer.ClientScript;
    using EPiServer.ClientScript.WebControls;
    using EPiServer.Core;
    using EPiServer.Core.PropertySettings;
    using EPiServer.DataAbstraction;
    using EPiServer.SpecializedProperties;
    using EPiServer.UI.Edit;
    using EPiServer.UI.WebControls;
    using EPiServer.Web.Hosting;
    using EPiServer.Web.PropertyControls;
    using EPiServer.Web.WebControls;

    /// <summary>
    /// Multiple property control
    /// </summary>
    /// <typeparam name="TEntity">The type of the entity.</typeparam>
    /// <typeparam name="TSingleEntity">The type of the single entity.</typeparam>
    public class MultiplePropertyControl<TEntity, TSingleEntity> : PropertyDataControl, IMultiplePropertyControl
        where TEntity : class, new()
        where TSingleEntity : class, new()
    {

        #region Members

        private PropertyDataCollection _propertyDataCollection;
        private Dictionary<string, IPropertyControl> _propertyControls;
        private Panel _container;
        private PlaceHolder _formPlaceHolder;
        private PlaceHolder _listControlsPlaceHolder;
        private bool _editTabClicked;

        private string _dataValue;
        private string _modeValue;
        private string _indexValue;
        private bool _saving;
        private bool _adding;
        private bool _editing;
        private bool _scrollIntoView;
        private bool _success;
        private bool? _isList;
        private readonly List<Control> _tabPanels = new List<Control>();
        private TabStrip _tabs;
        private readonly List<KeyValuePair<string, string>> _scriptBlocksToRegister = new List<KeyValuePair<string, string>>();
        private Control _topLevelTableCell;

        #endregion Members

        #region Properties

        /// <summary>
        /// Gets the property data collection.
        /// </summary>
        public PropertyDataCollection PropertyDataCollection
        {
            get { return _propertyDataCollection; }
        }

        /// <summary>
        /// Gets or sets the data value.
        /// </summary>
        /// <value>
        /// The data value.
        /// </value>
        public string DataValue
        {
            get
            {
                if (_dataValue != null)
                    return _dataValue;

                string key = string.Concat(PropertyData.Name, "_XmlDataValue");

                if (Page.Request.Form[key] != null)
                    _dataValue = MultiplePropertyHelper.DecompressString(Page.Request.Form[key]);

                return _dataValue ?? string.Empty;
            }
            set { _dataValue = value; }
        }

        /// <summary>
        /// Gets or sets the mode value.
        /// </summary>
        /// <value>
        /// The mode value.
        /// </value>
        public string ModeValue
        {
            get
            {
                if (_modeValue != null)
                    return _modeValue;

                string key = string.Concat(PropertyData.Name, "_ModeValue");

                if (Page.Request.Form[key] != null)
                    _modeValue = Page.Request.Form[key];

                return _modeValue ?? string.Empty;
            }
            set { _modeValue = value; }
        }

        /// <summary>
        /// Gets or sets the index value.
        /// </summary>
        /// <value>
        /// The index value.
        /// </value>
        private string IndexValue
        {
            get
            {
                if (_indexValue != null)
                    return _indexValue;

                string key = string.Concat(PropertyData.Name, "_IndexValue");

                if (Page.Request.Form[key] != null)
                    _indexValue = Page.Request.Form[key];

                return _indexValue ?? string.Empty;
            }
            set { _indexValue = value; }
        }

        /// <summary>
        /// Gets a value indicating whether [been initialised].
        /// </summary>
        /// <value>
        ///   <c>true</c> if [been initialised]; otherwise, <c>false</c>.
        /// </value>
        private bool BeenInitialised
        {
            get
            {
                string key = string.Concat(PropertyData.Name, "_BeenInitialised");

                if (Page.Request.Form[key] == null)
                    return false;

                return Page.Request.Form[key] == "1";
            }
        }

        /// <summary>
        /// Gets the sort order.
        /// </summary>
        private string SortOrder
        {
            get
            {
                string key = string.Concat(PropertyData.Name, "_SortOrder");
                return Page.Request.Form[key] == null ? string.Empty : Page.Request.Form[key].TrimEnd(new[] { ',' });
            }
        }

        /// <summary>
        /// Gets a value indicating whether this instance is list.
        /// </summary>
        /// <value>
        ///   <c>true</c> if this instance is list; otherwise, <c>false</c>.
        /// </value>
        public bool IsList
        {
            get
            {
                if (_isList.HasValue)
                    return _isList.Value;

                TEntity entity = new TEntity();
                _isList = MultiplePropertyHelper.IsList(entity);
                return _isList.Value;
            }
        }

        /// <summary>
        /// Gets a value indicating whether t his instance can enable controls.
        /// </summary>
        /// <value>
        /// <c>true</c> if this instance can enable controls; otherwise, <c>false</c>.
        /// </value>
        private bool CanEnableControls
        {
            get { return PropertyData.IsLanguageSpecific || CurrentPage.LanguageBranch == CurrentPage.MasterLanguageBranch; }
        }

        #endregion Properties

        #region Event handlers

        /// <summary>
        /// Add new click event
        /// </summary>
        /// <param name="sender">The sender.</param>
        /// <param name="e">The <see cref="System.EventArgs"/> instance containing the event data.</param>
        protected void AddNewClick(object sender, EventArgs e)
        {
            Add();
        }

        /// <summary>
        /// Saves click event
        /// </summary>
        /// <param name="sender">The sender.</param>
        /// <param name="e">The <see cref="System.EventArgs"/> instance containing the event data.</param>
        protected void SaveClick(object sender, EventArgs e)
        {
            Save();
        }

        /// <summary>
        /// Cancel click event
        /// </summary>
        /// <param name="sender">The sender.</param>
        /// <param name="e">The <see cref="System.EventArgs"/> instance containing the event data.</param>
        protected void CancelClick(object sender, EventArgs e)
        {
            Cancel();
        }

        /// <summary>
        /// Edit click event
        /// </summary>
        /// <param name="sender">The sender.</param>
        /// <param name="e">The <see cref="System.EventArgs"/> instance containing the event data.</param>
        protected void EditClick(object sender, EventArgs e)
        {
            Edit(int.Parse(((ToolButton)sender).CommandArgument));
        }

        /// <summary>
        /// Delete click event
        /// </summary>
        /// <param name="sender">The sender.</param>
        /// <param name="e">The <see cref="System.EventArgs"/> instance containing the event data.</param>
        protected void DeleteClick(object sender, EventArgs e)
        {
            Delete(int.Parse(((ToolButton)sender).CommandArgument));
        }

        protected void CopyAsNewClick(object sender, EventArgs e)
        {
            CopyAsNew(int.Parse(((ToolButton)sender).CommandArgument));
        }

        /// <summary>
        /// Moves up click event
        /// </summary>
        /// <param name="sender">The sender.</param>
        /// <param name="e">The <see cref="System.EventArgs"/> instance containing the event data.</param>
        protected void MoveUpClick(object sender, EventArgs e)
        {
            MoveUp(int.Parse(((ImageButton)sender).CommandArgument));
        }

        /// <summary>
        /// Moves down click event
        /// </summary>
        /// <param name="sender">The sender.</param>
        /// <param name="e">The <see cref="System.EventArgs"/> instance containing the event data.</param>
        protected void MoveDownClick(object sender, EventArgs e)
        {
            MoveDown(int.Parse(((ImageButton)sender).CommandArgument));
        }

        /// <summary>
        /// Changes the sort order click.
        /// </summary>
        /// <param name="sender">The sender.</param>
        /// <param name="e">The <see cref="System.EventArgs"/> instance containing the event data.</param>
        protected void ChangeSortOrderClick(object sender, EventArgs e)
        {
            ChangeSortOrder();
        }

        /// <summary>
        /// Raises the <see cref="E:System.Web.UI.Control.Init"/> event.
        /// </summary>
        /// <param name="e">An <see cref="T:System.EventArgs"/> object that contains the event data.</param>
        protected override void OnInit(EventArgs e)
        {
            base.OnInit(e);

            Initialise();
        }

        #endregion Event handlers

        /// <summary>
        /// Creates the place holders.
        /// </summary>
        private void CreatePlaceHolders()
        {
            Controls.Clear();
            _propertyDataCollection = new PropertyDataCollection();
            _propertyControls = new Dictionary<string, IPropertyControl>();

            //_container = new Panel { ID = PropertyData.Name + "_Container" };
            _container = new Panel { ID = "Container" };
            Controls.Add(_container);

            _formPlaceHolder = new PlaceHolder();
            _listControlsPlaceHolder = new PlaceHolder();
            _container.Controls.Add(_formPlaceHolder);
            _container.Controls.Add(_listControlsPlaceHolder);
        }

        /// <summary>
        /// Initialises the control.
        /// </summary>
        private void Initialise()
        {
            if (PropertyData == null)
                return;

            // Composer form falls over with viewstate loading errors
            if (HttpContext.Current.Handler.GetType().Name.Equals("dropit_plugin_extension_ui_edit_propertyeditor_aspx"))
                EnableViewState = false;

            _topLevelTableCell = MultiplePropertyHelper.GetTopLevelContainerTableCell(this);
            CreatePlaceHolders();

            AddCopyButton();

            // if the page is being posted back and a tab has been clicked reset fields
            if (Page.IsPostBack && Page.Request.Form["__EVENTARGUMENT"] != null && Page.Request.Form["__EVENTARGUMENT"].Equals("TabClicked"))
            {
                DataValue = PropertyData.Value != null ? PropertyData.Value.ToString() : string.Empty;
                ModeValue = string.Empty;
                IndexValue = string.Empty;
                _editTabClicked = true;
            }

            IMultiplePropertyControl topLevelMultiplePropertyControl = MultiplePropertyHelper.GetTopLevelMultiplePropertyControl(this) as IMultiplePropertyControl;

            if (topLevelMultiplePropertyControl != null && IsList)
            {
                PageDefinition pageDefinition = PageDefinition.Load(topLevelMultiplePropertyControl.PropertyData.PageDefinitionID);
                AddPropertyEditCaption(topLevelMultiplePropertyControl.PropertyData.Name, pageDefinition.EditCaption);
                MultiplePropertyEntityAttribute attribute = MultiplePropertyHelper.GetEntityAttribute(typeof(TEntity));

                if (MultiplePropertyConfiguration.GetConfiguration().ShowAsterixForRequiredFields && (pageDefinition.Required || (attribute.MinimumNumberOfEntitiesSet && attribute.MinimumNumberOfEntities > 0)))
                {
                    HtmlTableCell cell = this.Parent as HtmlTableCell;

                    if (cell != null)
                    {
                        HtmlTableRow row = cell.Parent as HtmlTableRow;

                        if (row != null)
                        {
                            cell = row.Cells[0];
                            HtmlGenericControl label = new HtmlGenericControl("span") { InnerHtml = "<span style=\"margin-left:3px\">*</span>" };
                            cell.Controls.Add(label);
                        }
                    }
                }
            }

            if (!IsList || (ModeValue != "1" && ModeValue != "2"))
                return;

            _adding = ModeValue == "1";
            _editing = ModeValue == "2";

            Type singularEntityType = MultiplePropertyHelper.GetSingularEntityType(new TEntity());
            object instance = Activator.CreateInstance(singularEntityType);
            CreateEditingControls(instance);

            CreateListControls();
        }

        /// <summary>
        /// Creates the edit controls.
        /// </summary>
        public override void CreateEditControls()
        {
            TEntity entity = GetEntityData();
            bool isList = MultiplePropertyHelper.IsList(entity);

            if (isList)
            {
                CreateListControls();
                LoadLinkCollectionState();
                return;
            }

            ModeValue = "1";
            CreateEditingControls(entity);
            LoadLinkCollectionState();
        }

        /// <summary>
        /// Creates the default controls.
        /// </summary>
        public override void CreateDefaultControls()
        {
            if (RenderType == RenderType.Default)
                ((MultiplePropertyBase<TEntity, TSingleEntity>)PropertyData).CreateDefaultViewControls(this);
            else
                base.CreateDefaultControls();
        }
        
        /// <summary>
        /// Creates a hidden form field.
        /// </summary>
        /// <param name="writer">The writer.</param>
        /// <param name="propertyName">Name of the property.</param>
        /// <param name="value">The value.</param>
        private void CreateHiddenFormField(HtmlTextWriter writer, string propertyName, string value)
        {
            writer.AddAttribute(HtmlTextWriterAttribute.Type, "hidden");
            writer.AddAttribute(HtmlTextWriterAttribute.Name, propertyName);
            writer.AddAttribute(HtmlTextWriterAttribute.Id, propertyName);
            writer.AddAttribute(HtmlTextWriterAttribute.Value, value);
            writer.RenderBeginTag(HtmlTextWriterTag.Input);
            writer.RenderEndTag();
        }

        /// <summary>
        /// Removes any duplication validation messages.
        /// </summary>
        /// <param name="validators">
        /// The validators.
        /// </param>
        private void RemoveDuplicateValidationMessagesAndTurnThemIntoLinks(List<StaticValidator> validators)
        {
            List<string> uniqueMessages = new List<string>();
            List<StaticValidator> validatorsToRemove = new List<StaticValidator>();

            foreach (StaticValidator validator in validators)
            {
                validator.EnableViewState = false;

                if (!uniqueMessages.Contains(validator.ErrorMessage))
                    uniqueMessages.Add(validator.ErrorMessage);
                else
                    validatorsToRemove.Add(validator);
            }

            foreach (StaticValidator validator in validatorsToRemove)
            {
                validator.IsValid = true;
                validator.Parent.Controls.Remove(validator);
            }

            validators.Clear();
            FindValidators(this, validators);

            foreach (StaticValidator validator in validators)
            {
                if (validator.ErrorMessage.Contains("<a href=\"") || !validator.ErrorMessage.Contains("\""))
                    continue;

                string start = validator.ErrorMessage.Substring(0, validator.ErrorMessage.IndexOf("\""));
                string middle = validator.ErrorMessage.Substring(validator.ErrorMessage.IndexOf("\"") + 1);

                if (!middle.Contains("\""))
                    continue;

                string end = middle.Substring(middle.IndexOf("\"") + 1);
                middle = middle.Substring(0, middle.IndexOf("\""));

                Control control = MultiplePropertyHelper.FindControlRecurisve(this, middle, false);

                if (control == null)
                    continue;

                validator.ErrorMessage = string.Format("\"{0}<a href=\"#{1}_ContainerCell\" style=\"text-decoration:underline!important\">{2}</a>\"{3}", start, control.ClientID, middle, end);
            }

            if (validators.Count > 0)
                EnhanceValidatorMessages(validators);
        }

        /// <summary>
        /// Finds StaticValidator controls.
        /// </summary>
        /// <param name="parent">The parent.</param>
        /// <param name="validators">The validators.</param>
        private void FindValidators(Control parent, ICollection<StaticValidator> validators)
        {
            foreach (Control child in parent.Controls)
            {
                if (child is StaticValidator)
                    validators.Add(child as StaticValidator);

                FindValidators(child, validators);
            }
        }

        /// <summary>
        /// Creates the list controls.
        /// </summary>
        private void CreateListControls()
        {
            TEntity entity = GetEntityData();
            bool isList = MultiplePropertyHelper.IsList(entity);

            if (!isList)
                return;

            _listControlsPlaceHolder.Controls.Clear();

            MultiplePropertyEntityAttribute attribute = MultiplePropertyHelper.GetEntityAttribute(typeof(TEntity));
            IMultiplePropertyEntityCollectionAbilityOverride entityCollectionAbilityOverride = entity as IMultiplePropertyEntityCollectionAbilityOverride;

            if (attribute.AllowAdd && (!attribute.MaximumNumberOfEntitiesSet || ((IList)entity).Count < attribute.MaximumNumberOfEntities) && (entityCollectionAbilityOverride == null || entityCollectionAbilityOverride.AllowAdd(CurrentPage, PropertyData.Name)))
            {
                Panel panel = new Panel { CssClass = "epi-buttonDefault" };
                panel.Style.Add("clear", "both");
                _listControlsPlaceHolder.Controls.Add(panel);

                ToolButton button = CreateToolButton(panel, "AddNewButton", !_adding && !_editing, "Add",
                                                        MultiplePropertyHelper.GetAddButtonText(attribute), AddNewClick);
                ScriptDisablePageLeaveEvent control = new ScriptDisablePageLeaveEvent { EventType = EventType.Click, EventTargetID = button.ID };
                panel.Controls.Add(control);
            }

            ShowItemsInList(entity);
        }

        /// <summary>
        /// Creates a new tool button.
        /// </summary>
        /// <param name="container">The container.</param>
        /// <param name="id">The id.</param>
        /// <param name="enabled">if set to <c>true</c> [enabled].</param>
        /// <param name="skinId">The skin id.</param>
        /// <param name="text">The text.</param>
        /// <param name="eventHandler">The event handler.</param>
        /// <returns>Creates a new tool button</returns>
        private ToolButton CreateToolButton(Control container, string id, bool enabled, string skinId, string text,
                                            EventHandler eventHandler)
        {
            ToolButton button = new ToolButton { ID = id, Enabled = enabled, SkinID = skinId, Text = text };
            button.Click += eventHandler;
            button.Enabled = CanEnableControls;
            container.Controls.Add(button);
            return button;
        }

        /// <summary>
        /// Creates the list form panels.
        /// </summary>
        /// <returns>Form panel</returns>
        private Control CreateListFormPanels()
        {
            // create edit container panel
            Panel editContainerPanel = new Panel();
            editContainerPanel.Style.Add("clear", "both");
            editContainerPanel.Style.Add("float", "left");
            editContainerPanel.Style.Add("width", "98%");
            editContainerPanel.Style.Add("border-left", "solid 1px #575757");
            editContainerPanel.Style.Add("border-right", "solid 1px #575757");
            editContainerPanel.Style.Add("border-bottom", "solid 1px #575757");
            editContainerPanel.Style.Add("margin-top", "10px");
            editContainerPanel.Style.Add("margin-bottom", "10px");

            // create heading container panel
            Panel headingContainerPanel = new Panel();
            editContainerPanel.Controls.Add(headingContainerPanel);
            headingContainerPanel.Style.Add("background-color", "#575757");
            headingContainerPanel.Style.Add("color", "White");
            headingContainerPanel.Style.Add("height", "20px");

            // create heading panel
            Panel headingPanel = new Panel();
            headingContainerPanel.Controls.Add(headingPanel);
            headingPanel.Style.Add("padding-left", "5px");
            headingPanel.Style.Add("verticle-align", "middle");
            headingPanel.Style.Add("padding-top", "2px");
            headingPanel.Style.Add("font-weight", "bold");
            headingPanel.Controls.Add(new Literal { Text = _adding ? Translate("new") : Translate("edit") });

            // create form Panel
            Panel formPanel = new Panel();
            editContainerPanel.Controls.Add(formPanel);
            formPanel.Style.Add("padding", "10px");
            formPanel.Style.Add("padding-bottom", "0px");

            _formPlaceHolder.Controls.Add(editContainerPanel);
            return formPanel;
        }

        /// <summary>
        /// Adds the copy button.
        /// </summary>
        private void AddCopyButton()
        {
            string propertyName = PropertyData.Name;

            if (propertyName.Contains("_"))
                propertyName = propertyName.Substring(0, propertyName.IndexOf("_"));

            bool scriptBlockRegistered = Page.ClientScript.IsClientScriptBlockRegistered(typeof(Page), propertyName);

            if (!MultiplePropertyHelper.GetEntityAttribute(typeof(TEntity)).AllowPropertyCopying ||
                scriptBlockRegistered ||
                CurrentPage.PageLink.ID <= 0 || PropertyData.IsDynamicProperty ||
                (IsList && ((IList)GetEntityData()).Count == 0))
                return;

            Panel panel = new Panel { CssClass = "epi-buttonDefault" };
            _container.Controls.AddAt(0, panel);

            ToolButton button = CreateToolButton(panel, "CopyButton", true, "Copy", Translate("copy"), null);
            panel.Controls.Add(button);
            button.OnClientClick =
                string.Format(
                    "javascript:window.open('{0}?PageId={1}&WorkId={2}&PropertyName={3}&Language={4}', 'MultiplePropertyCopy', 'width={5},height={6}');return false;",
                    "/util/ElencySolutionsMultipleProperty/MultiplePropertyCopyProperty.aspx",
                    CurrentPage.PageLink.ID,
                    CurrentPage.PageLink.WorkID,
                    PropertyData.Name,
                    CurrentPage.LanguageBranch,
                    550, 250);
            button.Enabled = CanEnableControls;

            Page.ClientScript.RegisterClientScriptBlock(typeof(Page), propertyName, string.Empty, false);
        }

        /// <summary>
        /// Creates the editing controls.
        /// </summary>
        /// <param name="entity">The entity.</param>
        private void CreateEditingControls(object entity)
        {
            if (_formPlaceHolder == null)
                CreatePlaceHolders();

            if (_formPlaceHolder != null)
                _formPlaceHolder.Controls.Clear();

            Control controlToAddTableTo = _formPlaceHolder;

            if (IsList)
                controlToAddTableTo = CreateListFormPanels();
            //else
            //{
            //    //Panel panel = new Panel();
            //    //_formPlaceHolder.Controls.Add(panel);
            //    //panel.CssClass = "epi-padding-small";
            //    //panel.Style["border"] = "1px solid #4D4D4D";
            //    //controlToAddTableTo = panel;
            //}


            List<PropertyInfo> properties = MultiplePropertyHelper.GetProperties(entity);
            Table table = new Table { CellSpacing = 0, CssClass = "epi-formLayout" };
            table.Style["display"] = "block";
            controlToAddTableTo.Controls.Add(table);

            List<IMultiplePropertyTab> multiplePropertyTabs;
            List<string> tabNames = MultiplePropertyHelper.GetTabNames(properties, out multiplePropertyTabs);
            _tabs = null;
            Panel tabsViewPanel = null;

            CreateTabs(out _tabs, out tabsViewPanel, tabNames, multiplePropertyTabs);

            if (_tabs == null)
            {
                foreach (PropertyInfo property in properties)
                    AddEditingRow(table, property, entity);
            }
            else
            {
                // add all rows to table that are not associated with a tab
                foreach (PropertyInfo property in properties.Where(current => string.IsNullOrEmpty(MultiplePropertyHelper.GetTabName(MultiplePropertyHelper.GetEntityPropertyAttribute(current)))))
                    AddEditingRow(table, property, entity);

                controlToAddTableTo.Controls.Add(tabsViewPanel);
                controlToAddTableTo.Controls.AddAt(controlToAddTableTo.Controls.Count - 1, _tabs);

                // then add tabs these don't need to be in table);
                int index = 1;
                foreach (string tabName in tabNames.Where(current => !string.IsNullOrEmpty(current)))
                {
                    Panel tabPanel = new Panel { ID = PropertyData.Name + "_TabViewPanel_" + index };
                    tabsViewPanel.Controls.Add(tabPanel);
                    _tabPanels.Add(tabPanel);

                    Table tabTable = new Table { CellSpacing = 0, CssClass = "epi-formLayout" };
                    table.Style["display"] = "block";
                    tabPanel.Controls.Add(tabTable);

                    string currentTabName = tabName;

                    foreach (
                        PropertyInfo property in
                            properties.Where(current => string.Equals(MultiplePropertyHelper.GetTabName(MultiplePropertyHelper.GetEntityPropertyAttribute(current)), currentTabName)))
                        AddEditingRow(tabTable, property, entity);

                    index++;
                }

                Tab selectedTab;

                int? selectedTabIndex = GetSelectedTabIndex(_tabs, out selectedTab);
                _tabs.SetSelectedTab(selectedTabIndex.HasValue ? selectedTabIndex.Value : 0);
            }

            if (!IsList)
                return;

            AddSaveAndCancelButtons(controlToAddTableTo, true);

            foreach (PropertyData propertyData in _propertyDataCollection)
                propertyData.InitializeData(CurrentPage.Property);
        }

        private int? GetSelectedTabIndex(TabStrip tabs, out Tab selectedTab)
        {
            selectedTab = null;

            if (tabs == null)
                return null;

            int? selectedTabIndex = null;
            int counter = -1;
            List<Tab> tabControls = tabs.Controls
                .Cast<Control>()
                .Where(current => current != null && current.GetType().Equals(typeof(Tab)))
                .Select(current => current as Tab)
                .ToList();

            foreach (Tab currentTab in tabControls)
            {
                counter++;
                string uniqueId = currentTab.UniqueID;

                if (Page.Request.Form.AllKeys.Any(current => string.Equals(current, uniqueId)) &&
                    string.Equals(Page.Request.Form[uniqueId], "TabClicked"))
                {
                    selectedTabIndex = counter;
                    selectedTab = currentTab;
                    break;
                }
            }

            return selectedTabIndex;
        }

        /// <summary>
        /// Adds the save and cancel buttons.
        /// </summary>
        /// <param name="container">The container.</param>
        /// <param name="bottom">if set to <c>true</c> [bottom].</param>
        private void AddSaveAndCancelButtons(Control container, bool bottom)
        {
            Panel panel = new Panel { CssClass = "epi-buttonDefault" };
            container.Controls.Add(panel);

            ToolButton button = CreateToolButton(panel, "SaveButton" + (bottom ? "Bottom" : "Top"), true, "Save",
                                                 _adding ? Translate("add") : Translate("update"),
                                                 SaveClick);
            button.CausesValidation = false;

            ScriptDisablePageLeaveEvent control = new ScriptDisablePageLeaveEvent { EventType = EventType.Click, EventTargetID = button.ID };
            panel.Controls.Add(control);

            button = CreateToolButton(panel, "CancelButton" + (bottom ? "Bottom" : "Top"), true, "Cancel", Translate("cancel"), CancelClick);
            button.CausesValidation = false;

            control = new ScriptDisablePageLeaveEvent { EventType = EventType.Click, EventTargetID = button.ID };
            panel.Controls.Add(control);
        }

        /// <summary>
        /// Creates the tabs.
        /// </summary>
        /// <param name="tabStrip">The tab strip.</param>
        /// <param name="tabsViewPanel">The tabs view panel.</param>
        /// <param name="tabNames">The tab names.</param>
        /// <param name="multiplePropertyTabs">The multiple property tabs.</param>
        private void CreateTabs(out TabStrip tabStrip, out Panel tabsViewPanel, ICollection<string> tabNames,
                                List<IMultiplePropertyTab> multiplePropertyTabs)
        {
            tabStrip = null;
            tabsViewPanel = null;

            if (tabNames.Count > 0)
            {
                tabStrip = new TabStrip
                               {
                                   ID = PropertyData.Name + "_Tabs",
                                   GeneratesPostBack = false,
                                   TargetID = PropertyData.Name + "_Tab_Panel"
                               };

                tabsViewPanel = new Panel { CssClass = "epi-padding-small" };
                tabsViewPanel.Style["border-left"] = "1px solid #4D4D4D";
                tabsViewPanel.Style["border-right"] = "1px solid #4D4D4D";
                tabsViewPanel.Style["border-bottom"] = "1px solid #4D4D4D";
                tabsViewPanel.ID = PropertyData.Name + "_Tab_Panel";
            }

            int index = 1;

            foreach (string tabName in tabNames.Where(current => !string.IsNullOrEmpty(current)))
            {
                IMultiplePropertyTab multiplePropertyTab = multiplePropertyTabs.First(current => string.Equals(current.TabName, tabName));

                Tab tab = new Tab
                              {
                                  Sticky = true,
                                  ID = PropertyData.Name + "_Tab_" + index,
                                  Text = tabName,
                                  Visible = CurrentPage.QueryDistinctAccess(multiplePropertyTab.RequiredAccess)
                              };

                tabStrip.Controls.Add(tab);
                index++;
            }
        }

        /// <summary>
        /// Adds an editing row.
        /// </summary>
        /// <param name="container">The container.</param>
        /// <param name="property">The property.</param>
        /// <param name="entity">The entity.</param>
        private void AddEditingRow(Table container, PropertyInfo property, object entity)
        {
            // add row panel
            TableRow tableRow = new TableRow();
            container.Rows.Add(tableRow);

            MultiplePropertyEntityPropertyAttribute attribute = MultiplePropertyHelper.GetEntityPropertyAttribute(property);
            MultiplePropertyEntityAttribute entityAttribute = null;
            
            if (MultiplePropertyHelper.HasEntityAttribute(attribute.Type))
                MultiplePropertyHelper.GetEntityAttribute(attribute.Type);

            // add label text
            TableHeaderCell headerCell = new TableHeaderCell { CssClass = "EP-tableCaptionCell" };
            string caption = MultiplePropertyHelper.GetCaption(attribute);
            HtmlGenericControl label = new HtmlGenericControl("span") { InnerHtml = caption };

            if (MultiplePropertyConfiguration.GetConfiguration().ShowAsterixForRequiredFields && (attribute.Required || (MultiplePropertyHelper.IsList(attribute.Type) && entityAttribute != null && (entityAttribute.MinimumNumberOfEntitiesSet && entityAttribute.MinimumNumberOfEntities > 0))))
                label.InnerHtml += "<span style=\"margin-left:3px\">*</span>";

            headerCell.Controls.Add(label);
            tableRow.Cells.Add(headerCell);

            string propertyName = GetPropertyName(property);
            
            AddPropertyEditCaption(propertyName, caption);
            _propertyDataCollection.Add(propertyName, Activator.CreateInstance(attribute.Type) as PropertyData);
            PropertyData propertyData = _propertyDataCollection[propertyName];

            SetPropertyValue(propertyData, property, entity, attribute);

            propertyData.IsRequired = attribute.Required;
            propertyData.IsLanguageSpecific = PropertyData.IsLanguageSpecific;
            AddPropertySettings(attribute, propertyData);

            IPropertyControl propertyControl = PropertyControlClassFactory.Instance.CreatePropertyControl(_propertyDataCollection[propertyName]);

            TableCell cell = new TableCell();
            cell.Style["padding-bottom"] = "0.3em";

            if (propertyControl.RowLayout == TableRowLayout.Wide)
            {
                tableRow = new TableRow();
                container.Rows.Add(tableRow);
                cell.ColumnSpan = 2;
            }

            tableRow.Cells.Add(cell);
            cell.ID = propertyName + "_ContainerCell";
            // call initialize data
            Control control = propertyControl as Control;
            control.ID = propertyName;
            propertyControl.PropertyData = propertyData;
            propertyControl.Properties = _propertyDataCollection;
            cell.Controls.Add(control);
            propertyControl.RenderType = RenderType.Edit;
            propertyControl.SetupControl();

            _propertyControls.Add(propertyName, propertyControl);
        }

        private void SetPropertyValue(PropertyData propertyData, PropertyInfo property,
            object entity, MultiplePropertyEntityPropertyAttribute attribute)
        {
            bool setValue = true;
            object value = property.GetValue(entity, null);

            // hack to code with moved or deleted images
            if (propertyData is PropertyImageUrl)
            {
                string filePath = value as string;

                if (!string.IsNullOrEmpty(filePath) && GenericHostingEnvironment.VirtualPathProvider.FileExists(value as string))
                    propertyData.Value = value;
                else
                    setValue = false;
            }
            else
                propertyData.Value = value;
            
            // this isn't right, because an int would have a default value of 0
            // maybe we need to check if it's new?

            if (_adding && attribute.DefaultValue != null)
                propertyData.Value = attribute.DefaultValue;

            if (!setValue || value == null) 
                return;

            try
            {
                propertyData.ParseToSelf(propertyData.Value.ToString());
            }
            catch
            {
                propertyData.Value = value;
            }
        }

        private IPropertySettings GetPropertySettings(MultiplePropertyEntityPropertyAttribute attribute,
                                                      PropertyData propertyData)
        {
            if (attribute.PropertySettingsCreator == null && !attribute.UseMatchingSettingsDecoratedOnPropertyType)
                return null;

            if (attribute.PropertySettingsCreator != null)
            {
                object instance = Activator.CreateInstance(attribute.PropertySettingsCreator);

                if (instance is IMultiplePropertySettingsCreator)
                    return ((IMultiplePropertySettingsCreator)instance).CreatePropertySettings();

                if (instance is IMultiplePropertySettingsCreatorAdvanced)
                    return ((IMultiplePropertySettingsCreatorAdvanced)instance).CreatePropertySettings(CurrentPage.PageTypeID, PropertyData.Name);

                return null;
            }

            if (attribute.UseMatchingSettingsDecoratedOnPropertyType)
            {
                // get the settings type this property can use
                IPropertyControl propertyControl = propertyData.CreatePropertyControl();

                if (propertyControl == null)
                    return null;

                // get attribute 
                PropertySettingsAttribute propertySettingsAttribute = propertyControl.GetType()
                                                                          .GetCustomAttributes(
                                                                              typeof(PropertySettingsAttribute), false)
                                                                          .FirstOrDefault() as PropertySettingsAttribute;

                if (propertySettingsAttribute == null)
                    return null;

                string propertyName = PropertyData.Name;
                string propertyDefinitionName = string.Empty;
                PageType pageType = PageType.Load(CurrentPage.PageTypeID);

                Dictionary<string, int> definitions = pageType.Definitions
                    .ToDictionary(pageDefinition => pageDefinition.Name, pageDefinition => pageDefinition.Name.Length);

                foreach (KeyValuePair<string, int> definition in definitions
                    .OrderByDescending(current => current.Value)
                    .Where(definition => propertyName.StartsWith(definition.Key + "_")))
                {
                    propertyDefinitionName = definition.Key;
                    break;
                }

                if (string.IsNullOrEmpty(propertyDefinitionName))
                    propertyDefinitionName = PropertyData.Name;

                PropertyData actualPropertyData = CurrentPage.Property[propertyDefinitionName];
                IPropertySettings settings = MultiplePropertyHelper.GetPropertySettings(actualPropertyData,
                                                                                        propertySettingsAttribute.
                                                                                            SettingsType);
                return settings;
            }

            return null;
        }

        /// <summary>
        /// Adds the property settings.
        /// </summary>
        /// <param name="attribute">The attribute.</param>
        /// <param name="propertyData">The property data.</param>
        private void AddPropertySettings(MultiplePropertyEntityPropertyAttribute attribute, PropertyData propertyData)
        {
            if (attribute.PropertySettingsCreator == null && !attribute.UseMatchingSettingsDecoratedOnPropertyType)
                return;

            IPropertySettings propertySettings = GetPropertySettings(attribute, propertyData);

            if (propertySettings == null)
                return;

            CreateSettingsContainer(propertyData);

            PropertySettingsWrapper wrapper = new PropertySettingsWrapper
                                                  {
                                                      IsGlobal = false,
                                                      PropertySettings = propertySettings
                                                  };
            propertyData.SettingsContainer.AddSettings(wrapper);
        }

        /// <summary>
        /// Creates the settings container.
        /// </summary>
        /// <param name="propertyData">The property data.</param>
        private void CreateSettingsContainer(PropertyData propertyData)
        {
            if (propertyData.SettingsContainer != null)
                return;

            List<FieldInfo> fields = new List<FieldInfo>();
            FindFields(fields, propertyData.GetType(), "_setting");

            if (fields.Count == 0)
                return;

            FieldInfo field = fields[0];

            if (field == null)
                return;

            field.SetValue(propertyData, new PropertySettingsContainer());
        }

        /// <summary>
        /// Finds class fields.
        /// </summary>
        /// <param name="fields">The fields.</param>
        /// <param name="type">The type.</param>
        /// <param name="fieldName">Name of the field.</param>
        private void FindFields(ICollection<FieldInfo> fields, Type type, string fieldName)
        {
            BindingFlags flags = BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance;

            foreach (var field in type.GetFields(flags))
            {
                if (field.Name != fieldName)
                    continue;

                fields.Add(field);
                break;
            }

            if (fields.Count > 0)
                return;

            Type baseType = type.BaseType;

            if (baseType != null)
                FindFields(fields, baseType, fieldName);
        }

        /// <summary>
        /// Gets the description text.
        /// </summary>
        /// <param name="attribute">The attribute.</param>
        /// <param name="entity">The entity.</param>
        /// <returns>Description text</returns>
        private string GetDescriptionText(MultiplePropertyEntityAttribute attribute, TEntity entity)
        {
            string descriptionText = MultiplePropertyHelper.GetInformationHeaderText(attribute);

            if (string.IsNullOrEmpty(descriptionText))
            {
                descriptionText = MultiplePropertyHelper.GetDescription(entity);

                if (string.IsNullOrEmpty(descriptionText))
                    descriptionText = Translate("info");
            }

            return descriptionText;
        }

        /// <summary>
        /// Adds the move up button.
        /// </summary>
        /// <param name="container">The container.</param>
        /// <param name="index">The index.</param>
        private void AddMoveUpButton(Control container, int index)
        {
            ImageButton button = new ImageButton
                                     {
                                         CommandArgument = index.ToString(),
                                         CausesValidation = false,
                                         ID = PropertyData.Name + "_UpButton_" + index,
                                         Enabled = !_adding && !_editing,
                                         ImageUrl =
                                             EPiServer.Web.PageExtensions.ThemeUtility.GetImageThemeUrl(Page,
                                                                                                        "Tools/Up.gif")
                                     };

            if (button.Enabled)
                button.Enabled = CanEnableControls;

            if (!button.Enabled)
            {
                button.Style["opacity"] = "0.5";
                button.Style["cursor"] = "default";
            }

            button.Click += MoveUpClick;
            container.Controls.Add(button);

            ScriptDisablePageLeaveEvent control = new ScriptDisablePageLeaveEvent { EventType = EventType.Click, EventTargetID = button.ID };
            container.Controls.Add(control);
        }

        /// <summary>
        /// Adds the move down button.
        /// </summary>
        /// <param name="container">The container.</param>
        /// <param name="index">The index.</param>
        private void AddMoveDownButton(Control container, int index)
        {
            ImageButton button = new ImageButton
                                     {
                                         CommandArgument = index.ToString(),
                                         CausesValidation = false,
                                         ID = PropertyData.Name + "_DownButton_" + index,
                                         Enabled = !_adding && !_editing,
                                         ImageUrl =
                                             EPiServer.Web.PageExtensions.ThemeUtility.GetImageThemeUrl(Page,
                                                                                                        "Tools/Down.gif")
                                     };

            if (button.Enabled)
                button.Enabled = CanEnableControls;

            if (!button.Enabled)
            {
                button.Style["opacity"] = "0.5";
                button.Style["cursor"] = "default";
            }

            button.Click += MoveDownClick;
            container.Controls.Add(button);

            ScriptDisablePageLeaveEvent control = new ScriptDisablePageLeaveEvent { EventType = EventType.Click, EventTargetID = button.ID };
            container.Controls.Add(control);
        }

        /// <summary>
        /// Shows the items in the entity list.
        /// </summary>
        /// <param name="entity">The entity.</param>
        private void ShowItemsInList(TEntity entity)
        {
            Table table = new Table { CssClass = "epi-default" };
            table.ID = PropertyData.Name + "_MultiplePropertyListingTable";
            table.Style.Add("margin-top", "5px");
            table.Style.Add("clear", "both");
            _listControlsPlaceHolder.Controls.Add(table);

            if (_adding || _editing || _saving)
                _scrollIntoView = true;

            TableRow tableRow = new TableRow { CssClass = "nodrop nodrag" };
            table.Rows.Add(tableRow);

            MultiplePropertyEntityAttribute attribute = MultiplePropertyHelper.GetEntityAttribute(typeof(TEntity));

            if (attribute.AllowSort)
                tableRow.Cells.Add(new TableHeaderCell { Text = Translate("sort") });

            tableRow.Cells.Add(new TableHeaderCell { Text = GetDescriptionText(attribute, entity) });

            if (attribute.AllowEdit)
            {
                tableRow.Cells.Add(new TableHeaderCell {Text = Translate("edit")});
                tableRow.Cells[tableRow.Cells.Count - 1].Style[HtmlTextWriterStyle.PaddingLeft] = "0.3em";
                tableRow.Cells[tableRow.Cells.Count - 1].Style[HtmlTextWriterStyle.PaddingRight] = "0.3em";
            }

            if (attribute.AllowDelete)
            {
                tableRow.Cells.Add(new TableHeaderCell {Text = Translate("delete")});
                tableRow.Cells[tableRow.Cells.Count - 1].Style[HtmlTextWriterStyle.PaddingLeft] = "0.3em";
                tableRow.Cells[tableRow.Cells.Count - 1].Style[HtmlTextWriterStyle.PaddingRight] = "0.3em";
            }

            if (attribute.AddCopyAsNewButton)
            {
                tableRow.Cells.Add(new TableHeaderCell {Text = Translate("copy")});
                tableRow.Cells[tableRow.Cells.Count - 1].Style[HtmlTextWriterStyle.PaddingLeft] = "0.3em";
                tableRow.Cells[tableRow.Cells.Count - 1].Style[HtmlTextWriterStyle.PaddingRight] = "0.3em";
            }

            bool changeSortOrderButtonAdded = false;

            IMultiplePropertyEntityCollectionAbilityOverride entityCollectionAbilityOverride = entity as IMultiplePropertyEntityCollectionAbilityOverride;

            for (int i = 0; i < ((IList)entity).Count; i++)
            {
                IMultiplePropertyEntityAbilityOverride entityAbilityOverride = ((IList)entity)[i] as IMultiplePropertyEntityAbilityOverride;

                tableRow = new TableRow();

                if (attribute.AllowSort && CanEnableControls && ((IList)entity).Count > 1 && !_adding && !_editing)
                    tableRow.Style.Add(HtmlTextWriterStyle.Cursor, "move");

                tableRow.Attributes.Add("rowindex", i.ToString());
                table.Rows.Add(tableRow);

                TableCell cell = new TableCell();

                if (attribute.AllowSort)
                {
                    cell.Style.Add(HtmlTextWriterStyle.TextAlign, "center");
                    cell.Style.Add(HtmlTextWriterStyle.Padding, "0.3em 0.4em");

                    if (_editing && i == int.Parse(IndexValue))
                        cell.Style["background-color"] = "#FAA61D!important";

                    tableRow.Cells.Add(cell);

                    if (i > 0)
                        AddMoveUpButton(cell, i);

                    if (i < (((IList) entity).Count - 1))
                        AddMoveDownButton(cell, i);
                }

                if (attribute.AllowSort && CanEnableControls && ((IList)entity).Count > 1 && !_adding && !_editing)
                {
                    bool scriptRegistered = Page.ClientScript.IsClientScriptBlockRegistered(typeof(Page), "TableDnd");

                    if (!scriptRegistered)
                        Page.ClientScript.RegisterStartupScript(typeof(Page), "TableDnd", string.Format("<script type=\"text/javascript\" src=\"{0}\"></script>", UriSupport.ResolveUrlFromUIBySettings("ClientResources/jquery.tablednd.js")), false);

                    string script =
                        @"$(document).ready(function() {
                            // Initialise the table
                            $('#" + ClientID + ClientIDSeparator +
                        PropertyData.Name +
                        @"_MultiplePropertyListingTable').tableDnD(
                                {
                                    onDragClass: 'epi-table-sortableRow-drag',
                                    onDrop: function(table, row) {
                                        var rows = table.tBodies[0].rows;
                                        var newpropertyOrder = '';
                                        var changed = false;
                                        var last = -1;
                                        for (var i = 0; i < rows.length; i++) {
                                            if ($(rows[i]).find('th').length > 0)
                                                continue;

                                            newpropertyOrder += $(rows[i]).attr('rowindex') + ',';
                                            var value = parseInt($(rows[i]).attr('rowindex'));

                                            if ((last + 1) != value)
                                                changed = true;

                                            last = value;
                                        }
                                        
                                        if (!changed)
                                            return;

                                        var prefix = $(table).attr('id');
                                        prefix = prefix.substr(0, prefix.lastIndexOf('_') + 1);
                                        $('#" +
                        PropertyData.Name +
                        @"_SortOrder').val(newpropertyOrder);
                                        $('#' + prefix + 'ChangeSortOrder').click();
                                    }
                                });});";

                    _scriptBlocksToRegister.Add(new KeyValuePair<string, string>(PropertyData.Name + "_MultiplePropertySortableScript", script));
                    //Page.ClientScript.RegisterStartupScript(typeof(Page), PropertyData.Name + "_MultiplePropertySortableScript", script, true);

                    if (!changeSortOrderButtonAdded)
                    {
                        Button button = new Button { ID = PropertyData.Name + "_ChangeSortOrder" };
                        button.Click += ChangeSortOrderClick;
                        button.Style.Add(HtmlTextWriterStyle.Display, "none");
                        _listControlsPlaceHolder.Controls.Add(button);
                        changeSortOrderButtonAdded = true;
                        ScriptDisablePageLeaveEvent disablePageLeaveEventControl = new ScriptDisablePageLeaveEvent
                                                                                       {
                                                                                           EventType = EventType.Click,
                                                                                           EventTargetID = button.ID
                                                                                       };
                        _listControlsPlaceHolder.Controls.Add(disablePageLeaveEventControl);
                    }
                }

                cell = new TableCell();
                cell.Style["border-color"] = string.IsNullOrEmpty(tableRow.CssClass)
                                                 ? string.Empty
                                                 : "#BEBEBE!important";
                cell.Style.Add(HtmlTextWriterStyle.Padding, "0.3em 0.4em");

                if (_editing && i == int.Parse(IndexValue))
                    cell.Style["background-color"] = "#FAA61D!important";

                tableRow.Cells.Add(cell);

                object item = ((IList)entity)[i];
                //MultiplePropertyHelper.HandleXhtmlStringPropertyTypes(item, true);
                string descrition = ((MultiplePropertyBase<TEntity, TSingleEntity>)PropertyData).GetListItemDescription((TSingleEntity)item);

                if (!string.IsNullOrEmpty(descrition))
                    cell.Controls.Add(new Literal { Text = descrition });
                else
                {
                    PropertyInfo descriptionProperty = MultiplePropertyHelper.GetDescriptionProperty(item);

                    if (descriptionProperty != null)
                    {
                        PropertyInfo propertyInfo = item.GetType().GetProperty(descriptionProperty.Name);

                        if (propertyInfo != null)
                        {
                            object value = propertyInfo.GetValue(item, null);

                            if (value != null)
                                cell.Controls.Add(new Literal { Text = value.ToString() });
                        }
                    }
                }

                // Add edit button
                if (attribute.AllowEdit)
                {
                    cell = new TableCell();

                    if (_editing && i == int.Parse(IndexValue))
                        cell.Style["background-color"] = "#FAA61D!important";

                    cell.Style.Add(HtmlTextWriterStyle.TextAlign, "center");
                    cell.Style.Add(HtmlTextWriterStyle.Padding, "0.3em 0.4em");
                    tableRow.Cells.Add(cell);
                    ToolButton toolButton = new ToolButton
                                                {
                                                    ID = PropertyData.Name + "_Edit_" + i,
                                                    Enabled = !_adding && !_editing,
                                                    CausesValidation = false,
                                                    CommandArgument = i.ToString(),
                                                    SkinID = "edit"
                                                };

                    if (toolButton.Enabled)
                        toolButton.Enabled = CanEnableControls;

                    if (toolButton.Enabled && ((entityCollectionAbilityOverride != null && !entityCollectionAbilityOverride.AllowEdit(CurrentPage, PropertyData.Name)) || (entityAbilityOverride != null && !entityAbilityOverride.AllowEdit(CurrentPage, PropertyData.Name))))
                        toolButton.Enabled = false;

                    toolButton.Click += EditClick;
                    cell.Controls.Add(toolButton);
                    ScriptDisablePageLeaveEvent scriptDisablePageLeaveEventControl = new ScriptDisablePageLeaveEvent
                                                                                         {
                                                                                             EventType = EventType.Click,
                                                                                             EventTargetID = toolButton.ID
                                                                                         };
                    cell.Controls.Add(scriptDisablePageLeaveEventControl);
                    tableRow.Cells.Add(cell);
                }

                if (attribute.AllowDelete)
                {
                    // Add delete button
                    cell = new TableCell();
                    cell.Style.Add(HtmlTextWriterStyle.TextAlign, "center");
                    cell.Style.Add(HtmlTextWriterStyle.Padding, "0.3em 0.4em");

                    if (_editing && i == int.Parse(IndexValue))
                        cell.Style["background-color"] = "#FAA61D!important";

                    tableRow.Cells.Add(cell);
                    ToolButton toolButton = new ToolButton
                                     {
                                         ID = PropertyData.Name + "_Delete_" + i,
                                         Enabled = !_adding && !_editing,
                                         CausesValidation = false,
                                         CommandArgument = i.ToString(),
                                         SkinID = "delete",
                                     };

                    if (toolButton.Enabled)
                        toolButton.Enabled = CanEnableControls;

                    if (toolButton.Enabled && ((entityCollectionAbilityOverride != null && !entityCollectionAbilityOverride.AllowDelete(CurrentPage, PropertyData.Name)) || (entityAbilityOverride != null && !entityAbilityOverride.AllowDelete(CurrentPage, PropertyData.Name))))
                        toolButton.Enabled = false;

                    toolButton.Click += DeleteClick;
                    cell.Controls.Add(toolButton);
                    ScriptDisablePageLeaveEvent scriptDisablePageLeaveEventControl = new ScriptDisablePageLeaveEvent { EventType = EventType.Click, EventTargetID = toolButton.ID };
                    cell.Controls.Add(scriptDisablePageLeaveEventControl);
                    tableRow.Cells.Add(cell);
                }

                // Add copy as new button
                if (attribute.AddCopyAsNewButton)
                {
                    cell = new TableCell();
                    cell.Style.Add(HtmlTextWriterStyle.TextAlign, "center");
                    cell.Style.Add(HtmlTextWriterStyle.Padding, "0.3em 0.4em");

                    if (_editing && i == int.Parse(IndexValue))
                        cell.Style["background-color"] = "#FAA61D!important";

                    tableRow.Cells.Add(cell);
                    ToolButton toolButton = new ToolButton
                    {
                        ID = PropertyData.Name + "_CopyAsNew_" + i,
                        Enabled = !_adding && !_editing,
                        CausesValidation = false,
                        CommandArgument = i.ToString(),
                        SkinID = "copy",
                    };

                    if (toolButton.Enabled)
                        toolButton.Enabled = CanEnableControls;

                    if (toolButton.Enabled && entityCollectionAbilityOverride != null && !entityCollectionAbilityOverride.AllowAdd(CurrentPage, PropertyData.Name))
                        toolButton.Enabled = false;

                    if (toolButton.Enabled && attribute.MaximumNumberOfEntitiesSet && ((IList)entity).Count >= attribute.MaximumNumberOfEntities)
                        toolButton.Enabled = false;

                    toolButton.Click += CopyAsNewClick;
                    cell.Controls.Add(toolButton);
                    ScriptDisablePageLeaveEvent scriptDisablePageLeaveEventControl = new ScriptDisablePageLeaveEvent { EventType = EventType.Click, EventTargetID = toolButton.ID };
                    cell.Controls.Add(scriptDisablePageLeaveEventControl);
                    tableRow.Cells.Add(cell);
                }
            }
        }

        /// <summary>
        /// Gets the name of the property.
        /// </summary>
        /// <param name="property">The property.</param>
        /// <returns>Property name</returns>
        private string GetPropertyName(PropertyInfo property)
        {
            return string.Concat(PropertyData.Name, "_", property.Name);
        }

        /// <summary>
        /// Applies edit changes
        /// </summary>
        public override void ApplyEditChanges()
        {
            TEntity entity = GetEntityData();

            bool isList = MultiplePropertyHelper.IsList(entity);
            object entityToUpdate = null;
            int itemIndex = -1;

            if (!isList || _saving)
            {
                entityToUpdate = entity;

                if (isList)
                {
                    Type singularEntityType = MultiplePropertyHelper.GetSingularEntityType(entity);

                    if (ModeValue == "1")
                    {
                        entityToUpdate = Activator.CreateInstance(singularEntityType);
                        ((IList)entity).Add(entityToUpdate);
                    }

                    if (ModeValue == "2")
                    {
                        int index = int.Parse(IndexValue);
                        itemIndex = index;
                        entityToUpdate = ((IList)entity)[index];
                    }
                }

                List<PropertyInfo> properties = MultiplePropertyHelper.GetProperties(entityToUpdate);

                foreach (PropertyInfo property in properties)
                {
                    string propertyName = GetPropertyName(property);

                    PropertyData propertyData = _propertyDataCollection[propertyName];

                    if (!_propertyControls.ContainsKey(propertyName))
                        continue;

                    IPropertyControl propertyControl = _propertyControls[propertyName];
                    bool propertyStringControl = false;

                    try
                    {
                        if (propertyControl is PropertyStringControl)
                        {
                            propertyStringControl = true;
                            (propertyControl as PropertyStringControl).ApplyEditChanges();
                        }
                        else
                            propertyControl.ApplyChanges();
                    }
                    catch (Exception)
                    {
                        if (propertyStringControl)
                            propertyControl.ApplyChanges();
                        else
                            throw;
                    }

                    object value = propertyData.Value;

                    if (value == null && property.GetGetMethod().ReturnType.Equals(typeof(bool)))
                        value = false;

                    if (value == null && property.GetValue(entityToUpdate, null) != null &&
                        property.GetValue(entityToUpdate, null) is string &&
                        property.GetValue(entityToUpdate, null).Equals(string.Empty))
                        value = string.Empty;

                    property.SetValue(entityToUpdate, value, null);
                }
            }

            ValidateEntityData(entity, isList, itemIndex, entityToUpdate);
            List<StaticValidator> validators = new List<StaticValidator>();
            FindValidators(this, validators);

            if (validators.Count > 0)
            {
                _success = false;
                return;
            }

            string xml;

            if (isList && !_saving)
                xml = DataValue;
            else
                xml = isList
                          ? MultiplePropertyHelper.SerializeObject(entity)
                          : MultiplePropertyHelper.SerializeObject(entityToUpdate);

            SetValue(xml);

            if (isList)
                DataValue = xml;
        }

        private void ValidateEntityData(TEntity entity, bool isList, int itemIndex, object entityToUpdate)
        {
            MultiplePropertyEntityAttribute attribute = MultiplePropertyHelper.GetEntityAttribute(typeof(TEntity));
            if (isList && attribute.MinimumNumberOfEntitiesSet &&
                ((IList)entity).Count < attribute.MinimumNumberOfEntities)
            {
                List<StaticValidator> validators = new List<StaticValidator> { new StaticValidator(PropertyData.Name) };
                EnhanceValidatorMessages(validators);

                string caption = validators[0].ErrorMessage;

                string errorMessage = string.Format(Translate("minimumNumberOfEntities"),
                                                    attribute.MinimumNumberOfEntities,
                                                    ((IList)entity).Count,
                                                    caption,
                                                    ((IList)entity).Count == 1 ? Translate("entity") : Translate("entities"),
                                                    ((IList)entity).Count == 1 ? Translate("has") : Translate("have"));
                AddErrorValidator(errorMessage);
            }

            string validationMessage = string.Empty;

            if (isList && !_saving)
            {
                validationMessage = ((MultiplePropertyBase<TEntity, TSingleEntity>)PropertyData).ValidateEntities(entity);

                if (!string.IsNullOrEmpty(validationMessage))
                {
                    List<StaticValidator> validators = new List<StaticValidator> { new StaticValidator(PropertyData.Name) };
                    EnhanceValidatorMessages(validators);

                    string caption = validators[0].ErrorMessage;

                    if (string.IsNullOrEmpty(caption))
                        caption = PropertyData.Name;

                    AddErrorValidator(string.Format("\"{0}\": {1}", caption, validationMessage));
                }
            }

            validationMessage = ((MultiplePropertyBase<TEntity, TSingleEntity>)PropertyData).ValidateEntity(entityToUpdate as TSingleEntity);

            if (!string.IsNullOrEmpty(validationMessage))
            {
                List<StaticValidator> validators = new List<StaticValidator> { new StaticValidator(PropertyData.Name) };
                EnhanceValidatorMessages(validators);

                string caption = validators[0].ErrorMessage;

                if (string.IsNullOrEmpty(caption))
                    caption = PropertyData.Name;

                if (isList)
                {
                    int position = ((IList)entity).Count;

                    if (itemIndex != -1)
                        position = itemIndex + 1;

                    validationMessage = string.Format("\"{0}\" {1} {2}: {3}", caption, Translate("itemNumber"),
                                                      position, validationMessage);
                }
                else
                {
                    validationMessage = string.Format("\"{0}\": {1}", caption, validationMessage);
                }

                AddErrorValidator(validationMessage);
            }
        }

        /// <summary>
        /// Creates the add editing form
        /// </summary>
        private void Add()
        {
            _adding = true;
            ModeValue = "1";
            _propertyControls = new Dictionary<string, IPropertyControl>();
            _propertyDataCollection = new PropertyDataCollection();
            _formPlaceHolder.Controls.Clear();

            TEntity entity = new TEntity();
            Type singularEntityType = MultiplePropertyHelper.GetSingularEntityType(entity);
            object instance = Activator.CreateInstance(singularEntityType);

            CreateEditingControls(instance);

            // empty all child multiple property controls
            List<Control> foundControls = new List<Control>();
            MultiplePropertyHelper.FindControlsOfType<IMultiplePropertyControl>(this, foundControls);

            foreach (Control control in foundControls)
            {
                ((IMultiplePropertyControl)control).DataValue = string.Empty;
            }
        }

        /// <summary>
        /// Saves the entity.
        /// </summary>
        private void Save()
        {
            _saving = true;
            _success = true;
            ApplyEditChanges();

            if (!_success)
                return;

            ModeValue = "0";
            _adding = false;
            _editing = false;
        }

        /// <summary>
        /// Cancels the current edit
        /// </summary>
        private void Cancel()
        {
            ModeValue = "0";
            _adding = false;
            _editing = false;
        }

        /// <summary>
        /// Deletes the specified index.
        /// </summary>
        /// <param name="index">The index.</param>
        private void Delete(int index)
        {
            ModeValue = "0";
            IList list = GetEntityData() as IList;
            list.RemoveAt(index);
            DataValue = list.ToString();
            _scrollIntoView = true;
        }

        /// <summary>
        /// Creates an edit form for the entity at the specified index
        /// </summary>
        /// <param name="index">The index.</param>
        private void Edit(int index)
        {
            _editing = true;
            ModeValue = "2";
            IndexValue = index.ToString();

            TEntity entity = GetEntityData();
            object item = ((IList)entity)[index];
            _propertyDataCollection = new PropertyDataCollection();
            _propertyControls = new Dictionary<string, IPropertyControl>();
            CreateEditingControls(item);
        }

        /// <summary>
        /// Copies as new.
        /// </summary>
        /// <param name="index">The index.</param>
        private void CopyAsNew(int index)
        {
            // does this need to apply changes etc???
            TEntity entity = GetEntityData();
            IList list = entity as IList;
            object item = list[index];
            list.Add(item);

            _dataValue = MultiplePropertyHelper.SerializeObject(entity);

            _propertyDataCollection = new PropertyDataCollection();
            _propertyControls = new Dictionary<string, IPropertyControl>();
            _editing = false;
            ModeValue = "0";
            _scrollIntoView = true;
        }

        /// <summary>
        /// Moves an entity up.
        /// </summary>
        /// <param name="index">The index.</param>
        private void MoveUp(int index)
        {
            ModeValue = "0";
            IList list = GetEntityData() as IList;
            object entity1 = list[index - 1];
            object entity2 = list[index];

            list.RemoveAt(index - 1);
            list.RemoveAt(index - 1);
            list.Insert(index - 1, entity1);
            list.Insert(index - 1, entity2);
            DataValue = list.ToString();
            _scrollIntoView = true;
        }

        /// <summary>
        /// Moves an entity down.
        /// </summary>
        /// <param name="index">The index.</param>
        private void MoveDown(int index)
        {
            ModeValue = "0";
            IList list = GetEntityData() as IList;
            object entity1 = list[index];
            object entity2 = list[index + 1];
            list.RemoveAt(index);
            list.RemoveAt(index);
            list.Insert(index, entity1);
            list.Insert(index, entity2);
            DataValue = list.ToString();
            _scrollIntoView = true;
        }

        /// <summary>
        /// Changes the sort order.
        /// </summary>
        private void ChangeSortOrder()
        {
            if (string.IsNullOrEmpty(SortOrder))
                return;

            List<int> newIndexes =
                SortOrder.Split(new[] { ',' }, StringSplitOptions.RemoveEmptyEntries).Select(current => int.Parse(current))
                    .ToList();
            IList currentList = GetEntityData() as IList;
            IList newList = new TEntity() as IList;

            foreach (int index in newIndexes)
                newList.Add(currentList[index]);

            DataValue = newList.ToString();
            ModeValue = "0";
            _scrollIntoView = true;
        }

        /// <summary>
        /// Gets the string value for the property
        /// </summary>
        /// <returns>Property value as a string</returns>
        private string GetRawValue()
        {
            return PropertyData.Value == null ? string.Empty : PropertyData.Value.ToString();
        }

        /// <summary>
        /// Gets the typed entity data
        /// </summary>
        /// <returns>Entity data</returns>
        private TEntity GetEntityData()
        {
            string xml = (_editTabClicked || !IsList || !BeenInitialised) ? GetRawValue() : DataValue;

            if (string.IsNullOrEmpty(xml))
                return new TEntity();

            try
            {
                return MultiplePropertyHelper.DeserializeObject<TEntity>(xml);
            }
            catch (Exception)
            {
                return new TEntity();
            }
        }

        /// <summary>
        /// Translates the specified key.
        /// </summary>
        /// <param name="key">The key.</param>
        /// <returns>Language specific text</returns>
        private string Translate(string key)
        {
            return LanguageHelper.Translate(string.Format("/elencySolutionsMultipleProperty/{0}", key));
        }

        /// <summary>
        /// Raises the PreRender event.
        /// </summary>
        /// <param name="e">The <see cref="System.EventArgs"/> instance containing the event data.</param>
        protected override void OnPreRender(EventArgs e)
        {
            base.OnPreRender(e);

            if (RenderType == RenderType.Default)
                return;

            if (IsList)
                CreateListControls();

            foreach (Panel panel in _tabPanels.Cast<Panel>().Where(panel => panel.Attributes["onclick"] != null))
                panel.Attributes.Remove("onclick");

            if (ModeValue == "0")
                _formPlaceHolder.Controls.Clear();

            StringBuilder builder = new StringBuilder();
            StringWriter stringWriter = new StringWriter(builder);
            HtmlTextWriter writer = new HtmlTextWriter(stringWriter);

            if (!BeenInitialised)
                DataValue = MultiplePropertyHelper.SerializeObject(GetEntityData());

            CreateHiddenFormField(writer, string.Concat(PropertyData.Name, "_XmlDataValue"),
                                  MultiplePropertyHelper.CompressString(DataValue));
            CreateHiddenFormField(writer, string.Concat(PropertyData.Name, "_ModeValue"), ModeValue);
            CreateHiddenFormField(writer, string.Concat(PropertyData.Name, "_IndexValue"), IndexValue);
            CreateHiddenFormField(writer, string.Concat(PropertyData.Name, "_BeenInitialised"), "1");
            CreateHiddenFormField(writer, string.Concat(PropertyData.Name, "_SortOrder"), string.Empty);
            PersistLinkCollectionState(writer);

            _formPlaceHolder.Controls.Add(new Literal { Text = builder.ToString() });

            List<StaticValidator> validators = new List<StaticValidator>();
            FindValidators(this, validators);

            if (_scrollIntoView && validators.Count == 0)
                Page.ClientScript.RegisterStartupScript(typeof(TEntity), Guid.NewGuid().ToString(), string.Format("$('#{0}').get(0).scrollIntoView();", ClientID + ClientIDSeparator + _container.ID), true);

            RemoveDuplicateValidationMessagesAndTurnThemIntoLinks(validators);

            if (_scriptBlocksToRegister.Count > 0 && CanEnableControls && !_adding && !_editing)
                Page.ClientScript.RegisterStartupScript(typeof(Page), _scriptBlocksToRegister[0].Key, _scriptBlocksToRegister[0].Value, true);

            Tab selectedTab;
            int? selectedTabIndex = GetSelectedTabIndex(_tabs, out selectedTab);
            List<string> tabsRegistered = new List<string>();

            if (selectedTabIndex.HasValue)
            {
                // this is called in onprerender of tab
                Page.ClientScript.RegisterHiddenField(selectedTab.UniqueID, "TabClicked");
            }
        }

        private void EnhanceValidatorMessages(List<StaticValidator> validators)
        {
            foreach (StaticValidator validator in validators)
            {
                string originalMessage = validator.ErrorMessage;
                string message = originalMessage;
                bool link = false;

                if (message.Contains("<a href=\""))
                {
                    link = true;
                    message = message.Substring(message.IndexOf(">") + 1);
                    message = message.Substring(0, message.IndexOf("</a>"));
                }

                if (message.StartsWith("\""))
                {
                    message = message.Substring(1);
                    message = message.Substring(message.IndexOf("\""));
                }

                string propertyName = message;
                List<string> propertyNames = propertyName.Split(new[] { '_' }, StringSplitOptions.RemoveEmptyEntries).ToList();

                if (propertyNames.Count == 0)
                    continue;

                List<string> friendlyPropertyNames = new List<string>(propertyNames);

                bool valid = true;

                string hierarchy = string.Empty;

                for (int i = 0; i < propertyNames.Count; i++)
                {
                    string name = propertyNames[i];
                    hierarchy += (i == 0 ? string.Empty : "_") + name;

                    if (GetPropertyEditCaption(hierarchy) == null)
                    {
                        valid = false;
                        continue;
                    }

                    friendlyPropertyNames[i] = GetPropertyEditCaption(hierarchy);
                }

                if (!valid || friendlyPropertyNames.Count == 0)
                    continue;

                string newMessage = friendlyPropertyNames.Aggregate(string.Empty, (current, part) => current + (part + " - ")).TrimEnd(' ', '-');

                if (link)
                    validator.ErrorMessage = validator.ErrorMessage.Replace(">" + message + "<", ">" + newMessage + "<");
                else
                    validator.ErrorMessage = validator.ErrorMessage.Replace(message, newMessage);
            }
        }

        private void PersistLinkCollectionState(HtmlTextWriter writer)
        {
            //TODO: Find out how this must be changed - simen
            // hack to deal with linkitemcollection and filtered page reference link items collection controls
            //if (!ReferenceEquals(this, _topLevelTableCell.Controls[0])) 
            //    return;

            List<Control> controls = new List<Control>();
            MultiplePropertyHelper.FindControlsOfType<PropertyLinkCollectionEditControl>(this, controls);

            if (controls.Count > 0)
            {
                foreach (Control currentControl in controls)
                {
                    LinkItemCollection linkItemCollection = ((PropertyLinkCollectionEditControl)currentControl).NewLinkCollection;

                    if (linkItemCollection.Count == 0)
                        continue;

                    StringBuilder stringBuilder = new StringBuilder("<links>");
                    foreach (LinkItem current in linkItemCollection)
                        stringBuilder.Append(current.ToPermanentLink());
                    stringBuilder.Append("</links>");
                    string xml = stringBuilder.ToString();
                    CreateHiddenFormField(writer, string.Concat(PropertyData.Name, "_", currentControl.UniqueID, "_State"), MultiplePropertyHelper.CompressString(xml));
                }
            }

            Type type = Type.GetType("FilteredPageReference.UserControls.PropertyLinkCollectionFilteredPageReferenceEditControl");

            if (type == null)
                return;

            controls = new List<Control>();
            MultiplePropertyHelper.FindControlsWhereTypeNameContainsText(this, controls, "PropertyLinkCollectionFilteredPageReferenceEditControl");

            if (controls.Count <= 0) 
                return;

            foreach (Control currentControl in controls)
            {
                PropertyInfo newLinkCollectionProperty = currentControl.GetType().GetProperty("NewLinkCollection", BindingFlags.Instance | BindingFlags.Public);

                if (newLinkCollectionProperty == null)
                    continue;

                LinkItemCollection linkItemCollection = newLinkCollectionProperty.GetValue(currentControl, null) as LinkItemCollection;

                if (linkItemCollection.Count == 0)
                    continue;

                StringBuilder stringBuilder = new StringBuilder("<links>");
                foreach (LinkItem current in linkItemCollection)
                    stringBuilder.Append(current.ToPermanentLink());
                stringBuilder.Append("</links>");
                string xml = stringBuilder.ToString();
                CreateHiddenFormField(writer, string.Concat(PropertyData.Name, "_", currentControl.UniqueID, "_State"), MultiplePropertyHelper.CompressString(xml));
            }
        }

        private void LoadLinkCollectionState()
        {
            //TODO: find out how this must be changed - simen
            //if (!ReferenceEquals(this, _topLevelTableCell.Controls[0]))
            //    return;

            List<Control> controls = new List<Control>();
            MultiplePropertyHelper.FindControlsOfType<PropertyLinkCollectionEditControl>(this, controls);

            if (controls.Count > 0)
            {
                foreach (Control currentControl in controls)
                {
                    string name = string.Concat(PropertyData.Name, "_", currentControl.UniqueID, "_State");

                    if (Page.Request.Form[name] == null)
                        continue;

                    string xml = Page.Request.Form[name].Trim();

                    if (string.IsNullOrEmpty(xml))
                        continue;

                    xml = MultiplePropertyHelper.DecompressString(xml);

                    LinkItemCollection linkItemCollection = new LinkItemCollection();
                    XmlDocument xmlDocument = new XmlDocument();
                    try
                    {
                        xmlDocument.LoadXml(xml);
                    }
                    catch (XmlException innerException)
                    {
                        throw new InvalidPropertyValueException(typeof(PropertyLinkCollection).ToString(), xml, innerException);
                    }
                    XmlNodeList elementsByTagName = xmlDocument.GetElementsByTagName("a");
                    foreach (XmlNode anchorNode in elementsByTagName)
                    {
                        linkItemCollection.Add(new LinkItem(anchorNode));
                    }

                    ((PropertyLinkCollectionEditControl)currentControl).OriginalLinkCollection = linkItemCollection;
                }
            }

            controls = new List<Control>();
            MultiplePropertyHelper.FindControlsWhereTypeNameContainsText(this, controls, "PropertyLinkCollectionFilteredPageReferenceEditControl");

            if (controls.Count <= 0)
                return;

            foreach (Control currentControl in controls)
            {
                string name = string.Concat(PropertyData.Name, "_", currentControl.UniqueID, "_State");

                if (Page.Request.Form[name] == null)
                    continue;

                string xml = Page.Request.Form[name].Trim();

                if (string.IsNullOrEmpty(xml))
                    continue;

                xml = MultiplePropertyHelper.DecompressString(xml);

                LinkItemCollection linkItemCollection = new LinkItemCollection();
                XmlDocument xmlDocument = new XmlDocument();
                try
                {
                    xmlDocument.LoadXml(xml);
                }
                catch (XmlException innerException)
                {
                    throw new InvalidPropertyValueException(currentControl.GetType().ToString(), xml, innerException);
                }
                XmlNodeList elementsByTagName = xmlDocument.GetElementsByTagName("a");
                foreach (XmlNode anchorNode in elementsByTagName)
                {
                    linkItemCollection.Add(new LinkItem(anchorNode));
                }

                PropertyInfo originalLinkCollectionProperty = currentControl.GetType().GetProperty("OriginalLinkCollection", BindingFlags.Instance | BindingFlags.Public);

                if (originalLinkCollectionProperty == null)
                    continue;

                originalLinkCollectionProperty.SetValue(currentControl, linkItemCollection, null);
            }
        }

        private string GetPropertyEditCaption(string propertyName)
        {
            IMultiplePropertyControl topLevelMultiplePropertyControl = MultiplePropertyHelper.GetTopLevelMultiplePropertyControl(this) as IMultiplePropertyControl;

            if (topLevelMultiplePropertyControl == null || HttpContext.Current == null)
                return null;

            string key = string.Format("ESMP_{0}_{1}_{2}", CurrentPage.PageTypeID, topLevelMultiplePropertyControl.PropertyData.Name, propertyName);
            Debug.WriteLine(key);

            if (HttpContext.Current.Items[key] == null)
                return null;

            return HttpContext.Current.Items[key] as string;
        }

        private void AddPropertyEditCaption(string propertyName, string editCaption)
        {
            IMultiplePropertyControl topLevelMultiplePropertyControl = MultiplePropertyHelper.GetTopLevelMultiplePropertyControl(this) as IMultiplePropertyControl;

            if (topLevelMultiplePropertyControl == null || HttpContext.Current == null)
                return;

            string key = string.Format("ESMP_{0}_{1}_{2}", CurrentPage.PageTypeID, topLevelMultiplePropertyControl.PropertyData.Name, propertyName);
            Debug.WriteLine(key);

            HttpContext.Current.Items[key] = editCaption;
        }

    }
}